﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using System.Net;
using FCS.GlobalComponents;
using System.Workflow;
using System.ServiceModel.Description;
using System.ServiceModel;
using System.IO;
using System.Timers;


namespace FCS.Communication
{
    
    // This creates a single instance of the CQP so that the WCF service will be able to maintain the restricted valves, active valves
    // and current threshold
    [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, AddressFilterMode = AddressFilterMode.Any)]
    /// <summary>
    /// The Command Priroity Queue (CPQ) is the central point that manages the fountain's state. It is responsible for
    /// * Running the fountain during the day
    /// * Turning it off at night
    /// * Deciding which connection has priority for control
    /// * Managing restricted valves
    /// * Reporting fountain state
    /// * Sending commands to the cRIO
    /// </summary>
    public class CommandPriorityQueue : ICPQ
    {
        // Priority threshold of commands to run
        CommandPriority _threshold = CommandPriority.patterns;
        EventLogWriter _eventLogWriter = new EventLogWriter("CPQ");

        // List of valves that are not allowed to run
        List<List<string>> _restrictedValves = new List<List<string>>();

        // Prevents kiosk from holding on to control for too long if it crashes
        Timer _kioskTimer = new Timer(30000);

        // access codes
        List<string> _codes = new List<string>();

        // Time range for fountain mode execution
        readonly TimeSpan START_TIME = new TimeSpan(7, 0, 0); // 7 AM
        readonly TimeSpan STOP_TIME = new TimeSpan(22, 0, 0); // 10 PM

        // cRIO controller
        static cRIORelay _cRIO;


        static List<string> _activeValves = new List<string>();
        static List<string> _activeWeirs = new List<string>();
        static List<string> _activeWinterComponents = new List<string>();

        List<List<string>> _validCommands = new List<List<string>>();
        bool _commandsInit = false;

        bool _iscRIOSet = false;

        // These are magic numbers for the cRIO packet
        const byte SouthEndMagicNumber = 1;
        const byte NorthEndMagicNumber = 2;
        const byte WeirMagicNumber = 3;
        const byte MisterMagicNumber = 6;
        const byte LightsMagicNumber = 7;

        object _monitor;

        /// <summary>
        /// Sets up a queuing system for packets to be sent to the cRIO
        /// </summary>
        public CommandPriorityQueue()
        {
            _monitor = new object();
            Init();
            _cRIO = new cRIORelay();
            List<string> northValve = new List<string> { "N0" };
            SendValveCommand("ON", northValve, CommandPriority.patterns);
        }

        /// <summary>
        /// Initializes system by setting up lists, authorization, and a kiosk control timer
        /// </summary>
        private void Init()
        {
            InitRestrictedValves();
            SetAuth();
            _kioskTimer.Elapsed += new ElapsedEventHandler(_kioskTimer_Elapsed);
        }

        /// <summary>
        /// Sets up the queueing system for packets to be sent to a device at the given end point
        /// </summary>
        /// <param name="address">IP address at which the device is listening</param>
        /// <param name="localPort">The local port that the returning packets will be received from</param>
        /// <param name="endPointPort">The remote port that the packets will be sent to</param>
        public CommandPriorityQueue(IPAddress address, int localPort, int endPointPort)
        {
            _cRIO = new cRIORelay(address, localPort, endPointPort);
            Init();
        }

        /// <summary>
        /// This reads in the possible valid commands from a config file. This way, it can understand what sub commands to call when it receives a command
        /// </summary>
        private void InitValidCommands()
        {
            StreamReader sr = null;
            string folderName = @"C:\FountainData\controls.txt";
            try
            {
                sr = new StreamReader(folderName);

                int index = -1;

                while (!sr.EndOfStream)
                {
                    string str = sr.ReadLine();

                    // found a header
                    if (str.StartsWith("%"))
                    {
                        _validCommands.Add(new List<string>());
                        index++;
                    }
                    // insert into the list
                    else
                    {
                        _validCommands.ElementAt(index).Add(str);
                    }
                }
                sr.Close();
                sr.Dispose();
                _commandsInit = true;
            }
            catch (IOException)
            {
                _eventLogWriter.WriteToLog(System.Diagnostics.EventLogEntryType.Error, "Unable to load controls at " + folderName);
            }
        }

        /// <summary>
        /// Builds the restircted valves lists. It is able to adjust to added and removed priorities as long as patterns is never 
        /// removed and is the lowest priority
        /// </summary>
        private void InitRestrictedValves()
        {
            // We are assuming that patterns will always be the lowest priority so that
            // this loop will create the proper number of restirced valve lists
            for (int i = 0; i <= (int)CommandPriority.patterns; i++)
            {
                _restrictedValves.Add(new List<string>());
            }
        }

        /// <summary>
        /// Takes a simplified command and repackages it to be sent to the cRIO if approved
        /// </summary>
        /// <param name="state">ON or OFF</param>
        /// <param name="command">Valves, weirs, or misters</param>
        /// <param name="cp">Priority Level</param>
        /// <param name="secCode">Passcode</param>
        /// <returns></returns>
        public bool SendCommand(string state, List<string> command, CommandPriority cp, string secCode)
        {
            // make sure the cRIO connection is open
            if (_cRIO == null)
                SetDefaultCRIOAddress();

            // make sure whatever is calling is authorized at this time to be manipulating the fountain state
            if (!IsAuthorized(secCode, cp))
                return false;

            // make sure the system is ready to start processing commands.
            if (!_commandsInit)
                InitValidCommands();

            List<string> valve = new List<string>();
            List<string> weir = new List<string>();
            List<string> winter = new List<string>();

            // remove all null entries
            command.RemoveAll(item => item == null);

            if (!IsFountainOff())
            {
                // make sure that there commands to check against
                if (_validCommands.Count != 3)
                {
                    _eventLogWriter.WriteToLog(System.Diagnostics.EventLogEntryType.Error, "The valid commands were not read in correctly");
                    return false;
                }

                foreach (string cmd in command)
                {
                    if (_validCommands.ElementAt(0).Contains(cmd))
                        valve.Add(cmd);
                    else if (_validCommands.ElementAt(1).Contains(cmd))
                        weir.Add(cmd);
                    else if (_validCommands.ElementAt(2).Contains(cmd))
                        winter.Add(cmd);
                    else if (cmd.Equals(""))
                    {
                        // empty string, do nothing
                    }
                    else
                    {
                        _eventLogWriter.WriteToLog(System.Diagnostics.EventLogEntryType.Warning, "Invalid command based on command rules: " + cmd);
                    }
                }

                // check to make sure that the calling priority is higher than the active priority
                if (!IsHigherThanOrEqualToThreshold(cp))
                {
                    return false;
                }


                bool result = true;
                // send the command to the fountain
                if (valve.Count != 0)
                    result &= SendValveCommand(state, valve, cp);
                if (weir.Count != 0)
                    result &= SendWeirCommand(state, weir, cp);
                if (winter.Count != 0)
                    result &= SendWinterCommand(state, winter, cp);
            }
            else // if the fountain should be off, turn it off
            {
                // the fountain should be off, no matter who was last in control
                ResetThreshold(CommandPriority.lmoc, secCode);

                valve.Clear();
                valve.Add("V*");
                valve.Add("H*");
                SendValveCommand("OFF", valve, CommandPriority.patterns);

                weir.Clear();
                weir.Add("W*");
                SendWeirCommand("OFF", weir, CommandPriority.patterns);


                // we still want the lights to work
                foreach (string cmd in command)
                {
                    if (_validCommands.ElementAt(2).Contains(cmd))
                        winter.Add(cmd);
                }

                bool result = false;

                if (winter.Count != 0)
                    result = SendWinterCommand(state, winter, cp);

                return result;
            }

            return true;
        }

        /// <summary>
        /// Assesses if a priority is higher or equal to the current level
        /// </summary>
        /// <param name="cp">Priority to be assessed</param>
        /// <returns></returns>
        private bool IsHigherThanOrEqualToThreshold(CommandPriority cp)
        {
            CommandPriority curr = GetThreshold();
            if (cp <= curr)
                return true;
            return false;
        }

        /// <summary>
        /// Determins if the fountain should be off. Note: in debug mode, this will always return false (so you can test late at night!).
        /// </summary>
        /// <returns></returns>
        private bool IsFountainOff()
        {
#if DEBUG
            return false;
#else
            return (DateTime.Now.TimeOfDay < START_TIME || DateTime.Now.TimeOfDay > STOP_TIME);
#endif
        }

        /// <summary>
        /// Exposed method that sets the cRIO to the default address
        /// </summary>
        /// <param name="secCode"></param>
        public void SetDefaultCRIOAddress(string secCode)
        {
            if (!IsAuthorized(secCode, (CommandPriority)(-1)))
                return;

            SetDefaultCRIOAddress();
        }

        /// <summary>
        /// Creates a connection to the cRIO using the default settings
        /// </summary>
        private void SetDefaultCRIOAddress()
        {
            lock (_monitor)
            {
                if (!_iscRIOSet)
                {
                    _cRIO = new cRIORelay();
                    _iscRIOSet = true;
                }
            }
        }

        /// <summary>
        /// Creates a connection to the cRIO using a custom connection
        /// </summary>
        /// <param name="address">Address to cRIO</param>
        /// <param name="localPort">Local port to send on</param>
        /// <param name="endPointPort">Remote port to attach to</param>
        /// <param name="secCode"></param>
        public void SetCRIOAddress(String address, int localPort, int endPointPort, string secCode)
        {
            if (!IsAuthorized(secCode, (CommandPriority)(-1)))
                return;

            IPAddress addr;
            bool success = IPAddress.TryParse(address, out addr);


            lock (_monitor)
            {
                if (!_iscRIOSet && success)
                {
                    _cRIO = new cRIORelay(addr, localPort, endPointPort);
                    _iscRIOSet = true;
                }
            }

            if (!success)
            {
                SetDefaultCRIOAddress(secCode);
            }
        }


        /// <summary>
        /// The controllers will send their commands here with a specified priority 
        /// </summary>
        /// <param name="b">The command to be sent to the cRIO</param>
        /// <param name="cp">The priority at which it was being sent at</param>
        /// <returns>True if the packet is sent, false otherwise</returns>
        public bool SendValveCommand(string state, List<string> command, CommandPriority cp)
        {
            UpdateThreshold(cp);

            CommandPriority threshold;

            lock (_monitor)
            {
                threshold = _threshold;
            }

            if (cp == threshold)
            {
                command = RemoveRestrictedValvesFromCommand(command, cp);
                byte[] b;
                try
                {
                    b = ParseValveCommand(state, command);
                    _cRIO.SendPacket(b);
                }
                catch (ArgumentException aex)
                {
                    string message = "The command priority " + cp + " failed with error " + aex.Message;
                    _eventLogWriter.WriteToLog(System.Diagnostics.EventLogEntryType.Error, message);
                    return false;
                }
                return true;
            }

            return false;
        }

        /// <summary>
        /// Converts the command from text to the bytes needed to talk to the cRIO. Throws ArgumentException if there is an error in a command.
        /// </summary>
        /// <see cref="http://maquina.codeplex.com/wikipage?title=cRIO%20Communication%20Protocol&referringTitle=Documentation"/>
        /// <param name="command"></param>
        /// <returns></returns>
        private byte[] ParseValveCommand(string state, List<string> command)
        {
            // set up the packet for the north and south ends
            byte[] packet = new byte[10];
            packet[0] = SouthEndMagicNumber;
            packet[5] = NorthEndMagicNumber;

            state = state.ToUpper();
            if (state != "ON" && state != "OFF")
            {
                _eventLogWriter.WriteToLog(System.Diagnostics.EventLogEntryType.Warning, "Invalid state: " + state);
                return packet;
            }

            lock (_monitor)
            {
                // deal with wild card for verticle valves
                if (command.Contains("V*"))
                {
                    command.RemoveAll(StartsWithV);
                    for (int i = 1; i <= 10; i++)
                    {
                        command.Add("V" + i);
                    }
                    command.Add("VR");
                    command.Add("VC");
                }

                // deal with wild car for horizontal valves
                if (command.Contains("H*"))
                {
                    command.RemoveAll(StartsWithH);
                    for (int i = 1; i <= 10; i++)
                    {
                        command.Add("H" + i);
                    }
                    command.Add("HR");
                    command.Add("HC");
                }

                // add commands
                foreach (string s in command)
                {
                    string sU = s.ToUpper();
                    if (!_activeValves.Contains(sU) && state == "ON")
                        _activeValves.Add(sU);
                    else if (_activeValves.Contains(sU) && state == "OFF")
                        _activeValves.Remove(sU);
                }


                // build the packet to be sent to the cRIO
                foreach (string s in _activeValves)
                {
                    string item = s.ToUpper();
                    item = item.Trim();

                    #region South End Parsing

                    if (item == "HC")
                        packet[1] += 8;
                    else if (item == "HR")
                        packet[1] += 4;
                    else if (item == "H10")
                        packet[1] += 2;
                    else if (item == "H9")
                        packet[1] += 1;
                    else if (item == "H8")
                        packet[2] += 128;
                    else if (item == "H7")
                        packet[2] += 64;
                    else if (item == "H6")
                        packet[2] += 32;
                    else if (item == "H5")
                        packet[2] += 16;
                    else if (item == "H4")
                        packet[2] += 8;
                    else if (item == "H3")
                        packet[2] += 4;
                    else if (item == "H2")
                        packet[2] += 2;
                    else if (item == "H1")
                        packet[2] += 1;

                    else if (item == "VC")
                        packet[3] += 8;
                    else if (item == "VR")
                        packet[3] += 4;
                    else if (item == "V10")
                        packet[3] += 2;
                    else if (item == "V9")
                        packet[3] += 1;
                    else if (item == "V8")
                        packet[4] += 128;
                    else if (item == "V7")
                        packet[4] += 64;
                    else if (item == "V6")
                        packet[4] += 32;
                    else if (item == "V5")
                        packet[4] += 16;
                    else if (item == "V4")
                        packet[4] += 8;
                    else if (item == "V3")
                        packet[4] += 4;
                    else if (item == "V2")
                        packet[4] += 2;
                    else if (item == "V1")
                        packet[4] += 1;
                    #endregion

                    #region North End Parsing
                    else if (item == "N0")
                    {
                        // N0 is appended always
                    }
                    else if (item == "N1")
                        packet[9] += 2;
                    else if (item == "N2")
                        packet[9] += 4;
                    else if (item == "N3")
                        packet[9] += 8;
                    else if (item == "N4")
                        packet[9] += 16;
                    else if (item == "N5")
                        packet[9] += 32;
                    else if (item == "N6")
                        packet[9] += 64;
                    else if (item == "N7")
                        packet[9] += 128;

                    else if (item == "N8")
                        packet[8] += 1;
                    else if (item == "N9")
                        packet[8] += 2;
                    else if (item == "N10")
                        packet[8] += 4;
                    else if (item == "N11")
                        packet[8] += 8;
                    else if (item == "N12")
                        packet[8] += 16;
                    else if (item == "N13")
                        packet[8] += 32;
                    else if (item == "N14")
                        packet[8] += 64;
                    else if (item == "N15")
                        packet[8] += 128;

                    else if (item == "N16")
                        packet[7] += 1;
                    else if (item == "N17")
                        packet[7] += 2;
                    else if (item == "N18")
                        packet[7] += 4;
                    else if (item == "N19")
                        packet[7] += 8;
                    else if (item == "N20")
                        packet[7] += 16;
                    else if (item == "N21")
                        packet[7] += 32;
                    else if (item == "N22")
                        packet[7] += 64;
                    else if (item == "N23")
                        packet[7] += 128;

                    else if (item == "N24")
                        packet[6] += 1;
                    #endregion

                    else
                    {
                        _eventLogWriter.WriteToLog(System.Diagnostics.EventLogEntryType.Warning, item + " is an invalid valve command and cannot be translated to a code the cRIO understands.");
                    }
                }
            }

            packet[9] += 1; // Activates N0 (legacy)
            return packet;
        }

        private bool StartsWithV(String s)
        {
            if (s.StartsWith("V"))
                return true;
            return false;
        }

        private bool StartsWithH(String s)
        {
            if (s.StartsWith("H"))
                return true;
            return false;
        }

        /// <summary>
        /// Adds restricted valves to a given command priority
        /// </summary>
        /// <param name="valveList"></param>
        /// <param name="cp"></param>
        public void AddRestrictedValves(List<string> valveList, CommandPriority cp, string secCode)
        {
            if (!IsAuthorized(secCode, cp))
            {
                _eventLogWriter.WriteToLog(System.Diagnostics.EventLogEntryType.FailureAudit, "Auth failed: " + Shared.GetCommandPriorityName(cp) + " " + secCode);
                return;
            }

            lock (_monitor)
            {
                foreach (string s in valveList)
                {
                    if (!_restrictedValves.ElementAt((int)cp).Contains(s))
                        _restrictedValves.ElementAt((int)cp).Add(s);
                }
            }
        }

        /// <summary>
        /// Removes restricted valves from a given command priority
        /// </summary>
        /// <param name="valveList"></param>
        /// <param name="cp"></param>
        public void RemoveRestrictedValves(List<string> valveList, CommandPriority cp, string secCode)
        {
            if (!IsAuthorized(secCode, cp))
                return;

            lock (_monitor)
            {
                foreach (string s in valveList)
                {
                    if (_restrictedValves.ElementAt((int)cp).Contains(s))
                        _restrictedValves.ElementAt((int)cp).Remove(s);
                }
            }
        }

        /// <summary>
        /// Returns the restircted valves for a given command priority
        /// </summary>
        /// <param name="cp"></param>
        /// <returns></returns>
        public List<string> GetRestrictedValves(CommandPriority cp)
        {
            lock (_monitor)
            {
                return _restrictedValves.ElementAt((int)cp);
            }
        }

        /// <summary>
        /// This will remove the valves from the packet that should not be activated
        /// </summary>
        /// <param name="command"></param>
        /// <param name="cp"></param>
        /// <returns></returns>
        private List<string> RemoveRestrictedValvesFromCommand(List<string> command, CommandPriority cp)
        {
            lock (_monitor)
            {
                foreach (string s in _restrictedValves.ElementAt((int)cp))
                {
                    if (command.Contains(s))
                        command.Remove(s);
                }
            }

            return command;
        }

        /// <summary>
        /// Allows for control over the weirs
        /// </summary>
        /// <param name="state"></param>
        /// <param name="command"></param>
        /// <param name="cp"></param>
        /// <returns></returns>
        public bool SendWeirCommand(string state, List<string> command, CommandPriority cp)
        {
            UpdateThreshold(cp);
            CommandPriority threshold;

            lock (_monitor)
            {
                threshold = _threshold;
            }

            if (cp == threshold)
            {
                byte[] b;
                try
                {
                    b = ParseWeirCommand(state, command);
                }
                catch (ArgumentException aex)
                {
                    string message = "The command priority " + cp + " failed with error " + aex.Message;
                    _eventLogWriter.WriteToLog(System.Diagnostics.EventLogEntryType.Error, message);
                    return false;
                }

                _cRIO.SendPacket(b);
                return true;
            }

            return false;
        }

        /// <summary>
        /// Processes a weir command into it's packet design
        /// </summary>
        /// <param name="state">ON or OFF</param>
        /// <param name="command">List of weirs to modify</param>
        /// <returns></returns>
        private byte[] ParseWeirCommand(string state, List<string> command)
        {
            byte[] packet = new byte[2];
            packet[0] = WeirMagicNumber;

            state = state.ToUpper();
            if (state != "ON" && state != "OFF")
            {
                _eventLogWriter.WriteToLog(System.Diagnostics.EventLogEntryType.Warning, "Invalid state: " + state);
                return packet;
            }
            lock (_monitor)
            {
                // add commands
                foreach (string s in command)
                {
                    string sU = s.ToUpper();
                    if (!_activeWeirs.Contains(sU) && state == "ON")
                        _activeWeirs.Add(sU);
                    else if (_activeWeirs.Contains(sU) && state == "OFF")
                        _activeWeirs.Remove(sU);
                    else if (sU == "*")
                    {
                        _activeWeirs.Clear();
                        _activeWeirs.Add(sU);
                        break;
                    }
                }

                // check for wild card
                if (_activeWeirs.Contains("W*") && state == "OFF")
                {
                    _activeWeirs.Clear();
                    byte[] empty = new byte[2];
                    empty[0] = WeirMagicNumber;
                    return empty;
                }
                else if (_activeWeirs.Contains("W*") && state == "ON")
                {
                    _activeWeirs.Clear();

                    for (int i = 1; i <= 3; i++)
                    {
                        _activeWeirs.Add("W" + i.ToString());
                    }
                }


                foreach (string s in _activeWeirs)
                {
                    string item = s.ToUpper();
                    item = item.Trim();

                    #region Weir Parsing
                    if (item == "W1")
                        packet[1] += 1;
                    else if (item == "W2")
                        packet[1] += 2;
                    else if (item == "W3")
                        packet[1] += 4;
                    #endregion

                    else
                    {
                        string errorMessage = item + " is an invalid weir command and cannot be translated to a code the cRIO understands.";
                        _eventLogWriter.WriteToLog(System.Diagnostics.EventLogEntryType.Warning, errorMessage);
                    }
                }
            }

            return packet;
        }


        /// <summary>
        /// Sends the commands to the lights and misters
        /// </summary>
        /// <param name="state"></param>
        /// <param name="command"></param>
        /// <param name="cp"></param>
        /// <returns></returns>
        public bool SendWinterCommand(string state, List<string> command, CommandPriority cp)
        {
            UpdateThreshold(cp);

            CommandPriority threshold;

            lock (_monitor)
            {
                threshold = _threshold;
            }

            if (cp == threshold)
            {
                byte[] b;
                try
                {
                    b = ParseWinterCommand(state, command);
                    _cRIO.SendPacket(b);
                }
                catch (ArgumentException aex)
                {
                    string message = "The command priority " + cp + " failed with error " + aex.Message;
                    _eventLogWriter.WriteToLog(System.Diagnostics.EventLogEntryType.Error, message);
                    return false;
                }


                return true;
            }

            return false;
        }

        /// <summary>
        /// Builds the command for the lights and misters
        /// </summary>
        /// <param name="state"></param>
        /// <param name="command"></param>
        /// <returns></returns>
        private byte[] ParseWinterCommand(string state, List<string> command)
        {
            byte[] packet = new byte[17];
            packet[0] = MisterMagicNumber;
            packet[2] = LightsMagicNumber;


            state = state.ToUpper();
            if (state != "ON" && state != "OFF")
            {
                _eventLogWriter.WriteToLog(System.Diagnostics.EventLogEntryType.Warning, "Invalid state: " + state);
                return packet;
            }


            lock (_monitor)
            {

                // add commands
                foreach (string s in command)
                {
                    string sU = s.ToUpper();
                    if (!_activeWinterComponents.Contains(sU) && state == "ON")
                        _activeWinterComponents.Add(sU);
                    else if (_activeWinterComponents.Contains(sU) && state == "OFF")
                        _activeWinterComponents.Remove(sU);
                    else if (sU == "*")
                    {
                        _activeWinterComponents.Clear();
                        _activeWinterComponents.Add(sU);
                        break;
                    }
                }

                // check for wild card
                if (_activeWinterComponents.Contains("L*") && state == "OFF")
                    _activeWinterComponents.Clear();
                else if (_activeWinterComponents.Contains("L*") && state == "ON")
                {
                    _activeWinterComponents.Clear();

                    for (int i = 1; i <= 14; i++)
                    {
                        _activeWinterComponents.Add("L" + i.ToString());
                    }
                }


                foreach (string s in _activeWinterComponents)
                {
                    string item = s.ToUpper();
                    item = item.Trim();

                    #region Mister Parsing
                    if (item == "MS")
                        packet[1] += 1;
                    else if (item == "MN")
                        packet[1] += 2;
                    #endregion

                    #region Light Control Parsing
                    else if (item == "L1")
                        packet[3] = 255;
                    else if (item == "L2")
                        packet[4] = 255;
                    else if (item == "L3")
                        packet[5] = 255;
                    else if (item == "L4")
                        packet[6] = 255;
                    else if (item == "L5")
                        packet[7] = 255;
                    else if (item == "L6")
                        packet[8] = 255;
                    else if (item == "L7")
                        packet[9] = 255;
                    else if (item == "L8")
                        packet[10] = 255;
                    else if (item == "L9")
                        packet[11] = 255;
                    else if (item == "L10")
                        packet[12] = 255;
                    else if (item == "L11")
                        packet[13] = 255;
                    else if (item == "L12")
                        packet[14] = 255;
                    else if (item == "L13")
                        packet[15] = 255;
                    else if (item == "L14")
                        packet[16] = 255;
                    #endregion

                    else
                    {
                        string errorMessage = item + " is an invalid winter command and cannot be translated to a code the cRIO understands.";
                        _eventLogWriter.WriteToLog(System.Diagnostics.EventLogEntryType.Warning, errorMessage);
                    }
                }
            }

            return packet;
        }

        /// <summary>
        /// Given the command priority of the next incoming packet, this resets the priority threshold so that the threshold is always at the highest level (i.e. lmoc)
        /// </summary>
        /// <param name="cp"></param>
        private void UpdateThreshold(CommandPriority cp)
        {
            lock (_monitor)
            {
                if (IsHigherThanOrEqualToThreshold(cp))
                    _threshold = cp;
            }

            if (cp == CommandPriority.kiosk)
            {
                _kioskTimer.Enabled = true;
            }
        }

        /// <summary>
        /// When the kiosk's time is up, everything is reset
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void _kioskTimer_Elapsed(object sender, ElapsedEventArgs e)
        {
            List<String> cmd = new List<string>();
            cmd.Add("V*");
            cmd.Add("H*");
            SendValveCommand("OFF", cmd, CommandPriority.kiosk);

            _kioskTimer.Enabled = false;
            ResetThreshold(CommandPriority.kiosk);
            _kioskTimer.Interval = 30000;
        }

        /// <summary>
        /// Gets the current priority threshold
        /// </summary>
        public CommandPriority GetThreshold()
        {
            lock (_monitor)
            {
                return _threshold;
            }
        }

        /// <summary>
        /// This will reset the threshold and allow lower priority commands to be run. This will be called when the kiosk or lmoc relase control
        /// </summary>
        public void ResetThreshold(CommandPriority cp, string secCode)
        {
            if (!IsAuthorized(secCode, cp))
                return;

            ResetThreshold(cp);
        }

        private void ResetThreshold(CommandPriority cp)
        {
            lock (_monitor)
            {
                if (cp <= _threshold)
                    _threshold = CommandPriority.patterns;
            }
        }

        /// <summary>
        /// Gets the last resource state reported from the cRIO. 
        /// </summary>
        /// <returns>Most recently received state of the cRIO</returns>
        public cRIOState GetResources()
        {
            if (_cRIO == null)
                SetDefaultCRIOAddress();

            List<bool[]> responce = _cRIO.GetResources();
            cRIOState state = new cRIOState();

            state.BollardStates = responce[0];
            state.PumpStates = responce[1];
            state.ReportedEnvironStates = responce[2];

            return state;
        }

        /// <summary>
        /// Returns the north and south end valves that are currently active
        /// </summary>
        /// <returns></returns>
        public List<string> GetActiveValves()
        {
            List<string> ret;
            string[] tmp;

            lock (_monitor)
            {
                tmp = new string[_activeValves.Count];
                _activeValves.CopyTo(tmp);
                ret = tmp.ToList();
            }

            return ret;
        }

        /// <summary>
        /// Returns the list of active weirs
        /// </summary>
        /// <returns></returns>
        public List<string> GetActiveWeirs()
        {
            List<string> ret;
            string[] tmp;

            lock (_monitor)
            {
                tmp = new string[_activeWeirs.Count];
                _activeWeirs.CopyTo(tmp);
                ret = tmp.ToList();
            }

            return ret;
        }

        /// <summary>
        /// Returns the list of active winter components (inlc N and S mister and lights)
        /// </summary>
        /// <returns></returns>
        public List<string> GetActiveWinterComponents()
        {
            List<string> ret;
            string[] tmp;

            lock (_monitor)
            {
                tmp = new string[_activeWinterComponents.Count];
                _activeWinterComponents.CopyTo(tmp);
                ret = tmp.ToList();
            }

            return ret;
        }

        /// <summary>
        /// Return the resource status from the cRIO
        /// </summary>
        /// <returns></returns>
        public FountainResources GetResourceStatus()
        {
            if (_cRIO == null)
                SetDefaultCRIOAddress();

            FountainResources resources = new FountainResources();
            List<bool[]> rawResources = _cRIO.GetResources();

            // Process Bollards
            bool[] bollards = rawResources[0];
            for (int i = 0; i < FountainConstants.BollardCount; i++)
            {
                resources.Add("B" + i, bollards[i]);
            }

            // Process Pumps
            bool[] pumps = rawResources[1];
            for (int i = 0; i < FountainConstants.PumpCount; i++)
            {
                resources.Add("P" + i, pumps[i]);
            }

            // Process Environmental States
            bool[] environ = rawResources[2];
            resources.Add("North End HW Disable", environ[0]);
            resources.Add("South End HW Disable", environ[1]);
            resources.Add("Sidewalk Water Level OK", environ[2]);
            resources.Add("Manhole Cover Closed", environ[3]);

            return resources;
        }


        /// <summary>
        /// Closes the connection to the cRIO or other endpoint. Should be called when done with CPQ to clean up.
        /// </summary>
        public void Close()
        {
            if (_cRIO != null)
            {
                _cRIO.Close();
                _cRIO = null;
            }
        }

        /// <summary>
        /// Read in the codes that repersent LMOC, Music, etc.
        /// </summary>
        private void SetAuth()
        {
            StreamReader sr = new StreamReader(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86) + @"\Enlight Fountain Control Group\Fountain Control Service\codes.txt");
            while (!sr.EndOfStream)
            {
                _codes.Add(sr.ReadLine());
            }
            sr.Close();
            sr.Dispose();
        }

        /// <summary>
        /// Check to make sure one is authorized
        /// </summary>
        /// <param name="secCode">the security code</param>
        /// <param name="cp">the level they are trying to access; -1 is doesn't matter</param>
        /// <returns></returns>
        private bool IsAuthorized(string secCode, CommandPriority cp)
        {
#if DEBUG
            return true;
#else
            /*bool ignorePriority = false;
            if ((int)cp == -1)
                ignorePriority = true;
            */

            int index = -1;
            if (_codes.Contains(secCode))
            {
                index = _codes.IndexOf(secCode);
                if (index > (int)cp)
                    return false;
                return true;
            }
            else
            {
                _eventLogWriter.WriteToLog(System.Diagnostics.EventLogEntryType.Warning, String.Format("Access to {0} attempted with access code {1} ", Shared.GetCommandPriorityName(cp), secCode));
                return false;
            }

            /*
            if (index <= (int)cp || ignorePriority)
                return true;
            else
                return false;
            */
#endif
        }
    }
}
